/*
 * Copyright (C) 2011 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package hellfire.system.graphics;

import hellfire.system.engine.Main;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;

import android.opengl.GLES20;
import android.opengl.Matrix;
import android.util.Log;

public class GLImage implements GLDrawable
{
	//Texture coordinates buffer
	public final static FloatBuffer texCoordBuffer;
    
	//Shader program ID
	protected int program;
    
	//Image resource ID
	protected int resourceId;
    
    //Position & Size properties
    protected GLVec2 pos, dim;
    
    //Mask color
    protected float[] maskColor; //not GLColor to avoid 'new' when feeding shader
    
    //Rotation (clockwise, in degrees)
    protected float rotation;
    
    //Cache for handles
    protected GLCache cache = new GLCache();
    
    // Setup of texture coords buffer
    public static float textureCoords[] = 
    	{
    	0.0f, 1.0f,		// top left		(V2)
    	0.0f, 0.0f,		// bottom left	(V1)	
    	1.0f, 1.0f,		// top right	(V4)
    	1.0f, 0.0f,		// bottom right	(V3)
    		
			
        };

    static 
    {
    	ByteBuffer bb = ByteBuffer.allocateDirect(textureCoords.length * 4);
        bb.order(ByteOrder.nativeOrder());
        texCoordBuffer = bb.asFloatBuffer();
        texCoordBuffer.put(textureCoords);
        texCoordBuffer.position(0);
    }
    
    
    public GLImage(int resource, GLVec2 pos, GLVec2 dim) 
    {
    	program = Main.UI.renderer.getShaderProgram(Shaders.pImage);
    	this.pos = pos;
    	this.dim = dim;
    	this.resourceId = resource;
    	this.maskColor = GLColor.WHITE.asFloatArray();
    	this.rotation = 0;
    }
    
    protected FloatBuffer getTexCoordBuffer()
    {
    	return texCoordBuffer;
    }
    
    public void setMaskColor(GLColor c)
    {
    	if(c == null) 
    		this.maskColor = GLColor.WHITE.asFloatArray();
    	else this.maskColor = c.asFloatArray();
    }
    
    /**
     * @return A new GLColor object. It is unique.
     */
    public GLColor getMaskColor()
    {
    	return new GLColor(this.maskColor);
    }
    
    public void setRotation(float rot)
    {
    	this.rotation = rot;
    }
    
    public float getRotation()
    {
    	return this.rotation;
    }
    
    public void setImageResource(int resource)
    {
    	this.resourceId = resource;
    	cache.set(0, Main.UI.renderer.ResourceIdToTexture(resourceId));
    }
    
    public int getImageResource()
    {
    	return this.resourceId;
    }
    
    public void draw(float[] matrix) 
    {
    	int mPositionHandle;
    	int mTexelHandle;
        int mColorHandle;
        int mMVPMatrixHandle;
        int mTextureHandle;
        int texture;
        
        GLRenderer.pushMatrix(matrix);
        Matrix.translateM(matrix, 0, pos.x(), pos.y(), 0);
        Matrix.rotateM(matrix, 0, rotation, 0, 0, 1);
    	Matrix.scaleM(matrix, 0, matrix, 0, dim.x(), dim.y(), 1);
        GLES20.glUseProgram(program);
    	
    	if((texture = cache.get(0)) < 0)
        	texture = cache.set(0, Main.UI.renderer.ResourceIdToTexture(resourceId));
    	
        if((mPositionHandle = cache.get(1)) < 0)
        	mPositionHandle = cache.set(1, GLES20.glGetAttribLocation(program, "aPosition"));
        
        if((mColorHandle = cache.get(2)) < 0)
        	mColorHandle = cache.set(2, GLES20.glGetUniformLocation(program, "uColor"));
        
        if((mTexelHandle = cache.get(3)) < 0)
        	mTexelHandle = cache.set(3, GLES20.glGetAttribLocation(program, "aTexCoord"));
        
        if((mTextureHandle = cache.get(4)) < 0)
        	mTextureHandle = cache.set(4, GLES20.glGetUniformLocation(program, "uTexture"));
        
        if((mMVPMatrixHandle = cache.get(5)) < 0) 
        	mMVPMatrixHandle = cache.set(5, GLES20.glGetUniformLocation(program, "uMVPMatrix"));
        
        // Enable a handle to the triangle vertices
        GLES20.glEnableVertexAttribArray(mPositionHandle);

        // Prepare the triangle coordinate data
        GLES20.glVertexAttribPointer(
                mPositionHandle, 3,
                GLES20.GL_FLOAT, false,
                12, GLRect.vertexBuffer);
        
        GLES20.glEnableVertexAttribArray(mTexelHandle);
        GLES20.glVertexAttribPointer(
        		mTexelHandle, 2,
                GLES20.GL_FLOAT, false,
                0, getTexCoordBuffer());
        
        GLES20.glUniform4fv(mColorHandle, 1, maskColor, 0);
        
        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture);
        GLES20.glUniform1i(mTextureHandle, 0);
        
        // get handle to shape's transformation matrix
        //GLRenderer.checkGLError("glGetUniformLocation");

        // Apply the projection and view transformation
        GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, matrix, 0);
        //GLRenderer.checkGLError("glUniformMatrix4fv");

        // Draw the square
        GLES20.glDrawElements(
                GLES20.GL_TRIANGLES, GLRect.drawOrder.length,
                GLES20.GL_UNSIGNED_SHORT, GLRect.drawListBuffer);

        // Disable vertex array
        GLES20.glDisableVertexAttribArray(mPositionHandle);
        GLES20.glDisableVertexAttribArray(mTexelHandle);
    	
        GLRenderer.popMatrix(matrix);
    }
    
    public boolean checkBounds(GLVec2 pos)
    {
    	return (pos.x() > this.pos.x() - this.dim.x()
    			&& pos.x() < this.pos.x() + this.dim.x()
    			&& pos.y() < this.pos.y() + this.dim.y()
    			&& pos.y() > this.pos.y() - this.dim.y());
    }
    
    public GLVec2 getPos() 
	{
		return pos;
	}
    
    public void setPos(GLVec2 pos) 
	{
		this.pos = pos;
	}
}